sCanvas.fn.extend(true, {
	
	//stores object with registered events
	events: {
		mousemove: [ ],
		mouseenter: [ ],
		mouseleave: [ ],
		mouseup: [ ],
		mousedown: [ ],
		click: [ ],
		dblclick: [ ]
	},

	//stores last coordinates during mousemove
	lastMovePoint: null,

	//registers object with event
	addEventObject: function(obj, event) {
		var sc = this;
		
		//TODO: handle already added object
		this.events[event].push(obj);

		if (event === "click" || event === "dblclick" || event === "mouseup" || event === "mousedown") {
			// < 2 becouse object has been already pushed few lines above
			if (this.events[event].length < 2) {
				this.front["on" + event] = function(e) {
					sc.checkClickEvent(e, event);
				};
			}
		} else if (event === "mousemove" || event === "mouseleave" || event === "mouseenter") {
			// < 2 becouse object has been already pushed few lines above
			if (this.events["mousemove"].length + this.events["mouseleave"].length + this.events["mouseenter"].length < 2) {
				this.front["onmousemove"] = function(e) {
					sc.checkMoveEvent(e);
				};
			}
		}
	},

	//unregisters object with event 
	removeEventObject: function(obj, event) {
		var i = this.events[event].indexOf(obj);
		if (i !== -1) {
			delete this.events[event][i];
			
			if (event === "click" || event === "dblclick" || event === "mouseup" || event === "mousedown") {
				if (sCanvas.isEmpty(this.events[event])) {
					this.front["on" + event] = null;
					this.events[event] = [ ];
				}
			} else if (event === "mousemove" || event === "mouseleave" || event === "mouseenter") {
				if (sCanvas.isEmpty(this.events["mousemove"]) && sCanvas.isEmpty(this.events["mouseleave"]) && sCanvas.isEmpty(this.events["mouseenter"])) {
					this.front["onmousemove"] = null;
					this.events["mousemove"] = [ ];
					this.events["mouseleave"] = [ ];
					this.events["mouseenter"] = [ ];
				}
			}
		}
	},

	//if click was performed over object, triggers its event
	checkClickEvent: function(e, event) {
		var i, obj,
			evt = this.getBrowserEventData(e);
		
		evt["canvasX"] = e.pageX - this.pos.left;
		evt["canvasY"] = e.pageY - this.pos.top;
		
		for (i in this.events[event]) {
			var obj = this.events[event][i];
			if (obj.isPointIn(evt["canvasX"], evt["canvasY"])) {
				obj.trigger(
					sCanvas.Event(event, evt)
				);
			}
		}
	},

	//if move was performed over object, triggers its event
	checkMoveEvent: function(e) {
		var i, obj,
			evt = this.getBrowserEventData(e);
		
		evt["canvasX"] = e.pageX - this.pos.left;
		evt["canvasY"] = e.pageY - this.pos.top;

		//TODO: optimize this
		if (this.events["mouseenter"].length > 0) {
			for (i in this.events["mouseenter"]) {
				var obj = this.events["mouseenter"][i];
				if (obj.isPointIn(evt["canvasX"], evt["canvasY"])
						&& (this.lastMovePoint === null || !obj.isPointIn(this.lastMovePoint.x, this.lastMovePoint.y))) {
					obj.trigger(
						sCanvas.Event("mouseenter", evt)
					);
				}
			}
		}

		if (this.events["mousemove"].length > 0) {
			for (i in this.events["mousemove"]) {
				var obj = this.events["mousemove"][i];
				if (obj.isPointIn(evt["canvasX"], evt["canvasY"])) {
					obj.trigger(
						sCanvas.Event("mousemove", evt)
					);
				}
			}
		}

		if (this.events["mouseleave"].length > 0) {
			for (i in this.events["mouseleave"]) {
				var obj = this.events["mouseleave"][i];
				if (!obj.isPointIn(evt["canvasX"], evt["canvasY"])
						&& (this.lastMovePoint !== null && obj.isPointIn(this.lastMovePoint.x, this.lastMovePoint.y))) {
					obj.trigger(
						sCanvas.Event("mouseleave", evt)
					);
				}
			}
		}

		this.lastMovePoint = { x: evt["canvasX"], y: evt["canvasY"]	};
	},
	
	//copies and returns some data from original browser object
	getBrowserEventData: function(e) {
		var i,
			result = { },
			data = (
				"pageX pageY screenX screenY altKey ctrlKey metaKey shiftKey view"
			).split(" ");
		
		result.timeStamp = sCanvas.now();
		
		for (i = 0; i < data.length; i++) {
			if (e[data[i]] !== undefined) {
				result[data[i]] = e[data[i]];
			}
		}
		
		return result;
	}

});



//object containing event data, it allows to manipulate event behaviour
var EventObject = function(type, data) {
	this.type = type;
	
	if (data !== undefined) {
		sCanvas.extend(this, data);
	}
	
	return this;
};

//stores event type
EventObject.prototype.type = "";

//if true prevents executing next event handler
EventObject.prototype.stopImmediateProp = false;

//if true prevents executing next event handler
EventObject.prototype.remove = false;

//prevents next event handlers from being executed
EventObject.prototype.stopImmediatePropagation = function() {
	this.stopImmediateProp = true;
	return this;
};

//returns true if executing next event handlers was stopped
EventObject.prototype.isImmediatePropagationStopped = function() {
	return this.stopImmediateProp;
};

//if called, handlers will be removed after their triggering
EventObject.prototype.removeHandlers = function() {
	this.remove = true;
	return this;
};

EventObject.prototype.isRemovingHandlers = function() {
	return this.remove;
};